package drazing;

import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.text.NumberFormat;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;

/**
 * Motor de renderizado
 * @author xcubo
 */
public class RenderEngine
{
    private double despx, despy;

    private ControladorCarrera race_ctrl;

    private Graphics gScr;
    private BufferStrategy bufferStrategy;

    private BufferedImage gas = null;
    private BufferedImage neu = null;
    private BufferedImage off = null;
    private ImageIcon alert = null;
    private Dimension dim;

    /**
     * Crea un nuevo motor
     * @param rc ControladorCarrera a renderizar
     * @param bs Estrategia de Buffer
     */
    public RenderEngine(ControladorCarrera rc, BufferStrategy bs)
    {
        race_ctrl = rc;
        bufferStrategy = bs;
        dim = race_ctrl.getDimension();

        try {gas = ImageIO.read(getClass().getResource("../resources/img/icon/alertfuel.png"));}
        catch (IOException e) {}
        try {neu = ImageIO.read(getClass().getResource("../resources/img/icon/alertire.png"));}
        catch (IOException e) {}
        try {off = ImageIO.read(getClass().getResource("../resources/img/icon/off.png"));}
        catch (IOException e) {}
    }

    /**
     * Renderiza el estado actual del juego
     * @return boolean Exito o no
     */
    public boolean paint()
    {
        try
        {
            gScr = bufferStrategy.getDrawGraphics();
            sceneGraph(gScr);
            gScr.dispose();
            if (!bufferStrategy.contentsLost())
                bufferStrategy.show();
            else
                System.out.println("Contents Lost");
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }

    /**
     * Renderización del grafo de escena
     * @param gScr Graphics sobre el que dibujar
     */
    private void sceneGraph(Graphics gScr)
    {
        Graphics2D g2 = (Graphics2D)gScr;
        
        despx = (race_ctrl.getMyCar().getX()) - (dim.width / 2);
        despy = (race_ctrl.getMyCar().getY()) - (dim.height / 2);

        g2.setColor(Color.black);

        //Dibujamos la pista
        g2.translate(-despx, -despy);
        paintCicuit(g2, race_ctrl.getCircuit());

        //Dibujo mi coche
        try {
            paintCar(g2, race_ctrl.getMyCar());
        }
        catch (Exception e)
        {
            System.exit(0);
        }

        //Dibujo el resto de coches
        for (int i = 1; i < race_ctrl.getNumCoches(); i++) {
            if (race_ctrl.getCar(i).esVisible() == true)
            {
                paintCar(g2, race_ctrl.getCar(i));
            }
        }

        //Pinto el obstaculo especial
        Obstaculo o = race_ctrl.getCircuit().getObstacles(0);
        g2.setStroke(new BasicStroke(1.0f));
        g2.setColor(Color.DARK_GRAY);
        g2.draw(o.getSahpe());
        g2.fill(o.getSahpe());
        Rectangle2D bridge = new Rectangle2D.Double(o.getX(), o.getY(), o.getWidth(), 140);
        TexturePaint tp = new TexturePaint(o.getImage(), bridge);
        g2.setPaint(tp);
        g2.fill(bridge);

        //Pinto el panel
        paintPanel(g2);
    }

    /**
     * Renderiza el circuito
     * @param g Graphics para dibujar
     * @param p Pista a renderizar
     */
    public void paintCicuit(Graphics g, Pista p)
    {
        Graphics2D g2 = (Graphics2D)g;

        //Pintamos el césped 
        TexturePaint tp = new TexturePaint(p.getTerrain().getImage(), p.getTerrain().getPattern());
        g2.setPaint(tp);
        g2.fill(p.getTerrain().getLienzo());

        //Asfalto
        Stroke pincel = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
        g2.setStroke(pincel);
        g2.setColor(Color.DARK_GRAY);
        g2.fill(p.getSurface(1));

        //Lineas pista
        g2.setColor(Color.WHITE);
        g2.setStroke(new BasicStroke(3.0f));
        g2.draw(p.getTrackBorders(0));
        g2.draw(p.getTrackBorders(1));

        g2.draw(new Line2D.Float(1350, 570, 1350, 590));
        g2.draw(new Line2D.Float(1345, 570, 1350, 570));
        g2.draw(new Line2D.Float(1345, 590, 1350, 590));

        g2.draw(new Line2D.Float(1300, 510, 1300, 530));
        g2.draw(new Line2D.Float(1295, 510, 1300, 510));
        g2.draw(new Line2D.Float(1295, 530, 1300, 530));

        g2.draw(new Line2D.Float(1250, 570, 1250, 590));
        g2.draw(new Line2D.Float(1245, 570, 1250, 570));
        g2.draw(new Line2D.Float(1245, 590, 1250, 590));

        g2.draw(new Line2D.Float(1200, 510, 1200, 530));
        g2.draw(new Line2D.Float(1195, 510, 1200, 510));
        g2.draw(new Line2D.Float(1195, 530, 1200, 530));

        BufferedImage bi = new BufferedImage (10,10,BufferedImage.TYPE_INT_RGB);
        Graphics2D bigr = bi.createGraphics();

        bigr.setColor(Color.white);
        Rectangle2D bk = new Rectangle2D.Float(0, 0, 10.0f, 10.0f);
        bigr.fill(bk);
        bigr.setColor(Color.black);
        Rectangle2D r1 = new Rectangle2D.Float(0, 0, 5.0f, 5.0f);
        Rectangle2D r2 = new Rectangle2D.Float(5, 5, 5.0f, 5.0f);
        bigr.fill(r1);
        bigr.fill(r2);

        Rectangle2D meta_frame = new Rectangle2D.Float(0, 0, 10.0f, 10.0f);
        TexturePaint meta_texture = new TexturePaint(bi, meta_frame);
        g2.setPaint(meta_texture);

        g2.fill(new Rectangle2D.Float(1400, 500, 10.0f, 100.0f));


        // Pianos
        /*g2.setPaint(Color.WHITE);
        g2.setStroke(new BasicStroke(7.0f));
        g2.draw(new CubicCurve2D.Float(1550.0f,500.0f, 1800.0f, 510.0f,  1900.0f, 525.0f,  1900.0f, 900.0f));
        g2.setColor(Color.RED);
        g2.setStroke(new BasicStroke(7.0f,BasicStroke.CAP_SQUARE,BasicStroke.JOIN_MITER,10.0f,punteo,2.0f));
        g2.draw(new CubicCurve2D.Float(1550.0f,500.0f, 1800.0f, 510.0f,  1900.0f, 525.0f,  1900.0f, 900.0f));*/

        //Pintamos los obstaculos
        for(int i=0;i<p.getNumObs();i++)
        {
            Obstaculo o = p.getObstacles(i);
            Shape cuerpo = o.getSahpe();
            BufferedImage img_src = o.getImage();
            switch (o.getType())
            {
                case 0:
                    //Este obstaculo es especial, es el puente y se renderiza en sceneGraph()
                    break;
                case 1:         //Valla horizontal
                    g2.setStroke(new BasicStroke(1.0f));
                    g2.setColor(Color.BLACK);
                    g2.draw(cuerpo);
                    tp = new TexturePaint(img_src, new Rectangle2D.Double(o.getX(), o.getY(), 100, 20));
                    g2.setPaint(tp);
                    g2.fill(cuerpo);
                    break;
                case 2:
                case 3:         //Valla vertical
                    g2.setStroke(new BasicStroke(1.0f));
                    g2.setColor(Color.BLACK);
                    g2.draw(cuerpo);
                    tp = new TexturePaint(img_src, new Rectangle2D.Double(o.getX(), o.getY(), 20, 100));
                    g2.setPaint(tp);
                    g2.fill(cuerpo);
                    break;
                case 4:
                    g2.setStroke(new BasicStroke(1.0f));
                    g2.setColor(Color.BLACK);
                    g2.draw(cuerpo);
                    tp = new TexturePaint(img_src, new Rectangle2D.Double(o.getX(), o.getY(), 100, 20));
                    g2.setPaint(tp);
                    g2.fill(cuerpo);
                    break;
                case 5:
                case 6:
                case 7:
                    g2.setStroke(new BasicStroke(1.0f));
                    g2.setColor(Color.BLACK);
                    g2.draw(cuerpo);
                    tp = new TexturePaint(img_src, new Rectangle2D.Double(o.getX(), o.getY(), o.getWidth(), o.getHeight()));
                    g2.setPaint(tp);
                    g2.fill(cuerpo);
                    break;
                case 8:
                    g2.setStroke(new BasicStroke(1.0f));
                    g2.setColor(Color.LIGHT_GRAY);
                    g2.draw(cuerpo);
                    g2.fill(cuerpo);
                    break;

            }

        }
    }

    /**
     * Renderiza un coche
     * @param g Graphics sobre el que dibujar
     * @param thisCar Coche a renderizar
     */
    public void paintCar(Graphics g, Car thisCar) 
    {
        Graphics2D g2 = (Graphics2D)g;
        AffineTransform rotate = new AffineTransform();
        double x = thisCar.getX();
        double y = thisCar.getY();


        //Rotación del coche
        double rotation = thisCar.getOrientacion()*Math.PI/32.0;

        rotate.setToRotation(rotation, x, y+12);
        g2.transform(rotate);
        
        AffineTransform d = new AffineTransform();
        d.setToTranslation(x-10, y);
        g2.transform(d);


        // Pintado del aleron delantero
        if (thisCar.isWingOK(Car.FrontWing))
        {
            g2.setColor(thisCar.getColor(2));
            g2.fill(new Rectangle2D.Double(3,0,14,4));
        }

        // Pintado del chasis
        GeneralPath structure = new GeneralPath();
        structure.moveTo(10.0f, 3.0f);
        structure.lineTo(6.0f, 12.0f);

        structure.curveTo(6.0f, 13.0f, 5.0f, 13.0f, 5.0f, 13.0f);
        structure.curveTo(6, 15, 7, 18, 8, 25);
        structure.lineTo(12, 25);

        structure.lineTo(14.0f, 12.0f);
        structure.lineTo(10.0f,3.0f);
        structure.closePath();
        Ellipse2D cockpit = new Ellipse2D.Float(8, 8, 4, 6);

        g2.setStroke(new BasicStroke(3.0f,BasicStroke.JOIN_BEVEL,BasicStroke.CAP_BUTT));
        g2.setColor(Color.BLACK);
        g2.draw(structure);
        g2.setColor(thisCar.getColor(1));
        g2.fill(structure);

        g2.setColor(Color.BLACK);
        g2.fill(cockpit);

        // Pintado de neumaticos
        if (thisCar.getTire(Car.LeftFrontTire).isActive())
            g2.fill(new Rectangle2D.Float(0, 3, 5, 6));
        if (thisCar.getTire(Car.LeftBackTire).isActive())
            g2.fill(new Rectangle2D.Float(0, 16, 5, 6));
        if (thisCar.getTire(Car.RightFrontTire).isActive())
            g2.fill(new Rectangle2D.Float(15, 3, 5, 6));
        if (thisCar.getTire(Car.RightBackTire).isActive())
            g2.fill(new Rectangle2D.Float(15, 16, 5, 6));
        
        // Pintado del aleron trasero
        if (thisCar.isWingOK(Car.BackWing))
        {
            g2.setColor(thisCar.getColor(3));
            g2.fill(new Rectangle2D.Double(2,20,16,5));
        }
        try { d.invert();}catch(Exception e) { System.exit(0);}
        g2.transform(d);
        try { rotate.invert();}catch(Exception e) { System.exit(0);}
        g2.transform(rotate);
    }

    /**
     * Pinta un panel de datos
     * @param g Graphics para dibujar 
     */
    public void paintPanel(Graphics g)
    {
        Graphics2D g2 = (Graphics2D)g;
        AffineTransform desp = new AffineTransform();
        desp.setToTranslation(despx, despy);
        g2.transform(desp);
        Car myCar = race_ctrl.getMyCar();
        
        Rectangle2D salpicadero = new Rectangle2D.Double(0, dim.height-200, dim.width, 200);
        double speed = myCar.getSpeed();
        double fuel = myCar.getFuel();
        double life = myCar.getTireLife();
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(0);
        nf.setMinimumIntegerDigits(2);
        Font f = new Font("Arial", Font.ITALIC, 18);

        //Pinto el panel
        g2.setColor(Color.BLACK);
        g2.draw(salpicadero);
        GradientPaint gp1 = new GradientPaint(0,dim.height-200,Color.BLACK, 0, dim.height, Color.GRAY);
        g2.setPaint(gp1);
        g2.fill(salpicadero);

         //Configuro el texto
        g2.setPaint(Color.WHITE);
        FontRenderContext frc = g2.getFontRenderContext();
        nf.setMaximumFractionDigits(0);
        nf.setMinimumIntegerDigits(2);

        //Velocímetro y cuentavueltas
        String kmh = nf.format(speed*3.6);
        TextLayout tl = new TextLayout(kmh+" Km/h", f, frc);
        g2.setColor(Color.WHITE);
        tl.draw(g2, 5, dim.height-75);
        TextLayout lp = new TextLayout("Vuelta: "+race_ctrl.getLaps(),f,frc);
        lp.draw(g2, 5, dim.height-100);

        //Contador de gasolina
        nf.setMaximumFractionDigits(1);
        nf.setMinimumIntegerDigits(1);
        String gaslit = nf.format(fuel);
        TextLayout fuel_tag = new TextLayout(gaslit+" litros", f, frc);
        g2.setColor(Color.WHITE);
        fuel_tag.draw(g2, 5, dim.height-50);

        //Panel Tiempos
        race_ctrl.getCrono().print(g2, 5, dim.height-175);
        TextLayout vuelta = null;
        TextLayout times = new TextLayout("Vueltas:",f,frc);
        TextLayout ultima = null;
        TextLayout penultima = null;
        TextLayout antepenultima = null;
        if (race_ctrl.getMyCar().getTime(0) == null)
        {
            vuelta = new TextLayout("Mejor Vuelta: ",f,frc);
            ultima = new TextLayout(" ",f,frc);
            penultima = new TextLayout(" ",f,frc);
            antepenultima = new TextLayout(" ",f,frc);
        }
        else if (race_ctrl.getMyCar().getTime(1) == null)
        {
            vuelta = new TextLayout("Mejor Vuelta: "+race_ctrl.getBestTime()+" ("+race_ctrl.getBestLap()+")",f,frc);
            ultima = new TextLayout(" ",f,frc);
            penultima = new TextLayout(" ",f,frc);
            antepenultima = new TextLayout(" ",f,frc);
        }
        else if (race_ctrl.getMyCar().getTime(2) == null)
        {
            vuelta = new TextLayout("Mejor Vuelta: "+race_ctrl.getBestTime()+" ("+race_ctrl.getBestLap()+")",f,frc);
            ultima = new TextLayout("("+(race_ctrl.getLaps()-1)+") - "+race_ctrl.getMyCar().getTime(race_ctrl.getLaps()-1).toString(),f,frc);
            penultima = new TextLayout(" ",f,frc);
            antepenultima = new TextLayout(" ",f,frc);
        }
        else if (race_ctrl.getMyCar().getTime(3) == null)
        {
            vuelta = new TextLayout("Mejor Vuelta: "+race_ctrl.getBestTime()+" ("+race_ctrl.getBestLap()+")",f,frc);
            ultima = new TextLayout("("+(race_ctrl.getLaps()-1)+") - "+race_ctrl.getMyCar().getTime(race_ctrl.getLaps()-1).toString(),f,frc);
            penultima = new TextLayout("("+(race_ctrl.getLaps()-2)+") - "+race_ctrl.getMyCar().getTime(race_ctrl.getLaps()-2).toString(),f,frc);
            antepenultima = new TextLayout(" ",f,frc);
        }
        else
        {
            vuelta = new TextLayout("Mejor Vuelta: "+race_ctrl.getBestTime()+" ("+race_ctrl.getBestLap()+")",f,frc);
            ultima = new TextLayout("("+(race_ctrl.getLaps()-1)+") - "+race_ctrl.getMyCar().getTime(race_ctrl.getLaps()-1).toString(),f,frc);
            penultima = new TextLayout("("+(race_ctrl.getLaps()-2)+") - "+race_ctrl.getMyCar().getTime(race_ctrl.getLaps()-2).toString(),f,frc);
            antepenultima = new TextLayout("("+(race_ctrl.getLaps()-3)+") - "+race_ctrl.getMyCar().getTime(race_ctrl.getLaps()-3).toString(),f,frc);
        }
        times.draw(g2, 125, dim.height-150);
        ultima.draw(g2, 150, dim.height-130);
        penultima.draw(g2, 150, dim.height-110);
        antepenultima.draw(g2, 150, dim.height-90);

        //Debugg Panel
        g2.draw(new Rectangle2D.Float(dim.width-150, dim.height-195, 145, 100));
        TextLayout letrero = new TextLayout("Debugg info",f,frc);
        letrero.draw(g2,dim.width-150, dim.height-180);
        letrero = new TextLayout("x: "+nf.format(myCar.getX()),f,frc);
        letrero.draw(g2,dim.width-150, dim.height-165);
        letrero = new TextLayout("y: "+nf.format(myCar.getY()),f,frc);
        letrero.draw(g2,dim.width-150, dim.height-150);
        letrero = new TextLayout("pixel speed: "+nf.format(myCar.getSpeed()),f,frc);
        letrero.draw(g2,dim.width-150, dim.height-135);
        nf.setMinimumFractionDigits(2);
        letrero = new TextLayout("Tire Life: "+nf.format(myCar.getTireLife()),f,frc);
        letrero.draw(g2,dim.width-150, dim.height-120);
        letrero = new TextLayout("Sector: "+race_ctrl.getSector(),f,frc);
        letrero.draw(g2,dim.width-150, dim.height-105);


        //Pilotos Control
        

        if (!myCar.isOk())
        {
            alert = new ImageIcon(off.getScaledInstance(50, 50, Image.SCALE_SMOOTH));
            g2.drawImage(alert.getImage(), dim.width-500, dim.height-100, null);
        }
        if (fuel < 10)
        {
            System.out.println("Fuel Alert!!!");
            alert = new ImageIcon(gas.getScaledInstance(50, 50, Image.SCALE_SMOOTH));
            g2.drawImage(alert.getImage(), dim.width-600, dim.height-100, null);
        }
        if (life < 0.2)
        {
            alert = new ImageIcon(neu.getScaledInstance(50, 50, Image.SCALE_SMOOTH));
            g2.drawImage(alert.getImage(), dim.width-700, dim.height-100, null);
        }
        g2.setColor(Color.ORANGE);
        if (race_ctrl.isOffRoad())
            g2.fill(new Rectangle2D.Float(dim.width-450, dim.height-50, 20, 10));
        g2.setPaint(Color.ORANGE);
        vuelta.draw(g2,150, dim.height-185);

        try { desp.invert();}catch(Exception e) { System.exit(0);}
        g2.transform(desp);
    }


}
